Option Strict On
Option Explicit On

'Copyright (c) 2007-2010, William Severance, Jr., WESNet Designs
'All rights reserved.

'Redistribution and use in source and binary forms, with or without modification, are permitted
'provided that the following conditions are met:

'Redistributions of source code must retain the above copyright notice, this list of conditions
'and the following disclaimer.

'Redistributions in binary form must reproduce the above copyright notice, this list of conditions
'and the following disclaimer in the documentation and/or other materials provided with the distribution.

'Neither the name of William Severance, Jr. or WESNet Designs may be used to endorse or promote
'products derived from this software without specific prior written permission.

'Disclaimer: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
'            OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
'            AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE
'            FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
'            LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
'            INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
'            OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
'            IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'Although I will try to answer questions regarding the installation and use of this software when
'such questions are submitted via e-mail to the below address, no promise of further
'support or enhancement is made nor should be assumed.

'Developer Contact Information:
'     William Severance, Jr.
'     WESNet Designs
'     679 Upper Ridge Road
'     Bridgton, ME 04009
'     Phone: 207-647-9375
'     E-Mail: bill@wesnetdesigns.com
'     Website: www.wesnetdesigns.com

Imports Microsoft.VisualBasic
Imports System.Reflection
Imports System.IO
Imports System.Xml
Imports DotNetNuke
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities
Imports System.Globalization


Namespace WESNet.DNN.Modules.EPrayer

  Public Class MailManager
    Inherits DotNetNuke.Services.Mail.Mail

    Public Enum MailSendMethods
      [To]
      CC
      BCC
    End Enum

#Region "Private Members"
    Private _PostAction As Enums.PostActions
    Private _PostID As Integer
    Private _Post As PostInfo

    Private _ModerationAction As Enums.ModerationActions
    Private _Comment As String = String.Empty

    Private _UserInfo As UserInfo
    Private _UserID As Integer

    Private _ModuleId As Integer
    Private _TabID As Integer
    Private _TabModuleID As Integer

    Private _RequestID As Integer = -1
    Private _Request As RequestInfo

    Private _AuthorID As Integer
    Private _AuthorInfo As UserInfo

    Private _Recipients As New Generic.List(Of String)
    Private _RecipientLists As New Generic.List(Of String)
    Private _MaxRecipientListLength As Integer = 1200

    Private _PortalId As Integer
    Private _PortalSettings As Entities.Portals.PortalSettings
    Private _HostSettings As Dictionary(Of String, String)

    Private _MyConfiguration As Configuration
    Private _TemplatePath As String
#End Region

#Region "Public Properties"

    Public Property PostAction() As Enums.PostActions
      Get
        Return _PostAction
      End Get
      Set(ByVal value As Enums.PostActions)
        _PostAction = value
      End Set
    End Property

    Public Property PostID() As Integer
      Get
        Return _PostID
      End Get
      Set(ByVal value As Integer)
        _PostID = value
        Initialize()
      End Set
    End Property

    Public ReadOnly Property Post() As PostInfo
      Get
        Return _Post
      End Get
    End Property

    Public Property ModerationAction() As Enums.ModerationActions
      Get
        Return _ModerationAction
      End Get
      Set(ByVal value As Enums.ModerationActions)
        _ModerationAction = value
      End Set
    End Property

    Public ReadOnly Property RequestID() As Integer
      Get
        Return _RequestID
      End Get
    End Property

    Public ReadOnly Property Request() As RequestInfo
      Get
        Return _Request
      End Get
    End Property

    Public Property Comment() As String
      Get
        Return _Comment
      End Get
      Set(ByVal value As String)
        _Comment = value
      End Set
    End Property

    Public Property Recipients() As Generic.List(Of String)
      Get
        Return _Recipients
      End Get
      Set(ByVal value As Generic.List(Of String))
        _Recipients = value
      End Set
    End Property

    Public ReadOnly Property MyConfiguration() As Configuration
      Get
        If _MyConfiguration Is Nothing Then
          Dim obj As Object = DataCache.GetCache(Consts.ConfigurationCacheKey)
          If obj Is Nothing Or Consts.ConfigurationCacheDuration = 0 Then
            _MyConfiguration = New Configuration(_ModuleId)
            DataCache.SetCache(Consts.ConfigurationCacheKey, _MyConfiguration, New TimeSpan(0, 0, Consts.ConfigurationCacheDuration))
          Else
            _MyConfiguration = CType(obj, WESNet.DNN.Modules.EPrayer.Configuration)
          End If
        End If
        Return _MyConfiguration
      End Get
    End Property

    Public ReadOnly Property PortalSettings() As Entities.Portals.PortalSettings
      Get
        If _PortalSettings Is Nothing Then
          _PortalSettings = Entities.Portals.PortalController.GetCurrentPortalSettings()
        End If
        Return _PortalSettings
      End Get
    End Property

    Public ReadOnly Property TemplatePath() As String
      Get
        If _TemplatePath Is Nothing Then
          If MyConfiguration.EnablePerPortalTemplates Then
            Dim _path As String = PortalSettings.HomeDirectory & MyConfiguration.ModulePath.Replace(Common.ApplicationPath & "/DesktopModules/", "") & "Templates/"
            If Directory.Exists(HttpContext.Current.Server.MapPath(_path)) Then
              _TemplatePath = _path
            End If
          End If
          If _TemplatePath Is Nothing Then
            _TemplatePath = MyConfiguration.ModulePath & "Templates/"
          End If
        End If
        Return _TemplatePath
      End Get
    End Property

#End Region

#Region "Constructors"
    Public Sub New()
      MyBase.New()
    End Sub

    Public Sub New(ByVal PostAction As Enums.PostActions, ByVal PostID As Integer)
      Me.New(PostAction, PostID, Enums.ModerationActions.None, String.Empty)
    End Sub

    Public Sub New(ByVal PostAction As Enums.PostActions, ByVal PostID As Integer, ByVal ModerationAction As Enums.ModerationActions)
      Me.New(PostAction, PostID, ModerationAction, String.Empty)
    End Sub

    Public Sub New(ByVal PostAction As Enums.PostActions, ByVal PostID As Integer, ByVal ModerationAction As Enums.ModerationActions, ByVal Comment As String)
      MyBase.New()
      _PostAction = PostAction
      _PostID = PostID
      _ModerationAction = ModerationAction
      _Comment = Comment
      Initialize()
    End Sub

#End Region

#Region "Private Methods"
    Private Sub Initialize()
      Try
        _UserID = Entities.Users.UserController.GetCurrentUserInfo.UserID
      Catch exc As Exception
        _UserID = -1
      End Try

      _PortalId = PortalSettings.PortalId
      _HostSettings = Host.Host.GetHostSettingsDictionary()

      If _PostID = -1 Then
        Throw New ArgumentException("Unable to intialize EPrayer Mail Manager - PostID cannot be -1")
      Else
        Dim epc As New EPrayerController
        _Post = epc.GetPost(_PostID)
        _AuthorID = _Post.CreatedByUserID
        If _Post Is Nothing Then
          Throw New ArgumentNullException("Unable to initialize EPrayer Mail Manager - Invalid PostID")
        Else
          _RequestID = _Post.RequestID
          _Request = epc.GetRequest(_RequestID, _UserID, True)
          If _Request Is Nothing Then
            Throw New ArgumentNullException("Unable to initialize EPrayer Mail Manager - Invalid RequestID")
          Else
            _ModuleId = _Request.ModuleID
            _TabID = PortalSettings.ActiveTab.TabID
            Dim mc As New ModuleController
            Dim mi As ModuleInfo = mc.GetModule(_ModuleId, _TabID)
            _TabModuleID = mi.TabModuleID
            _UserInfo = epc.GetUser(_ModuleId, _UserID)
            If _AuthorID = _UserID Then
              _AuthorInfo = _UserInfo
            Else
              _AuthorInfo = epc.GetUser(_ModuleId, _AuthorID)
            End If
          End If
        End If
      End If
    End Sub
#End Region

#Region "Public Methods"

    Public Sub AddRecipient(ByVal MailTo As String)
      If Not _Recipients.Contains(MailTo) Then
        _Recipients.Add(MailTo)
      End If
    End Sub

    Public Sub AddRecipients(ByVal MailToList As Generic.LinkedList(Of String))
      For Each MailTo As String In MailToList
        AddRecipient(MailTo)
      Next
    End Sub

    Public Sub SendBulkEMail(ByVal MessageKey As String)

      Dim MailTo As String
      Dim CC As String = String.Empty
      Dim BCC As String = String.Empty

      If Recipients.Count = 0 Then Exit Sub

      Dim Subject As String = GetText(MessageKey & ".Subject")

      Dim Body As String = GetText(MessageKey & ".Body")

      Dim MailSendMethod As MailSendMethods = MyConfiguration.MailSendMethod

      If MailSendMethod = MailSendMethods.CC Or MailSendMethod = MailSendMethods.BCC Then
        Dim tmp As New StringBuilder
        For Each MailTo In Recipients
          If (tmp.Length + 1 + MailTo.Length) > _MaxRecipientListLength Then
            _RecipientLists.Add(tmp.ToString)
            tmp = New StringBuilder(MailTo)
          Else
            tmp.Append(MailTo)
            tmp.Append(";")

          End If
        Next
        If tmp.Length > 0 Then _RecipientLists.Add(tmp.ToString)
      End If

      Select Case MyConfiguration.MailSendMethod
        Case MailSendMethods.To
          For Each MailTo In Recipients
            SendMail(MailTo, "", "", Subject, Body, "")
          Next
        Case MailSendMethods.BCC
          For Each RecipientList As String In _RecipientLists
            MailTo = RecipientList.Substring(0, RecipientList.IndexOf(";"c))
            BCC = RecipientList.Remove(0, MailTo.Length + 1).TrimEnd(";"c)
            SendMail(MailTo, "", BCC, Subject, Body, "")
          Next
        Case MailSendMethods.CC
          For Each RecipientList As String In _RecipientLists
            MailTo = RecipientList.Substring(0, RecipientList.IndexOf(";"c))
            CC = RecipientList.Remove(0, MailTo.Length + 1).TrimEnd(";"c)
            SendMail(MailTo, CC, "", Subject, Body, "")
          Next
      End Select
    End Sub

    Public Overloads Sub SendMail(ByVal MailTo As String, ByVal CC As String, ByVal BCC As String, _
                                  ByVal Subject As String, ByVal Body As String, ByVal Attachment As String)

      Dim MailFrom As String
      Dim MailFormat As DotNetNuke.Services.Mail.MailFormat

      If MyConfiguration.UseAdminEmailAsSender Then
        MailFrom = MyConfiguration.AdminEmail
      Else
        MailFrom = _UserInfo.Email
      End If

      MailFormat = MyConfiguration.MailFormat

      If MailFormat = DotNetNuke.Services.Mail.MailFormat.Html Then
        ' Add Base Href for any images inserted in to the email.
        Body = "<base href='" & AddHTTP(HttpContext.Current.Request.ServerVariables("HTTP_HOST").TrimEnd("/"c)) & "'>" & Body
      Else
        Body = Utilities.StripHTMLTags(Body)
      End If

      SendMail(MailFrom, MailTo, CC, BCC, DotNetNuke.Services.Mail.MailPriority.Normal, Subject, MailFormat, _
                  System.Text.Encoding.UTF8, Body, Attachment, "", "", "", "")
    End Sub

    'Send to subject if send encouragement checked and valid e-mail or
    'to EPrayer Admin EMail a request to send encouraging card by regular mail if no
    'valid e-mail but valid postal address
    Private Sub SendSubjectEMails()
      Dim Subject As String
      Dim Body As String

      If MyConfiguration.EnableSubjectContact AndAlso Post.NotifySubject AndAlso Not Request.SubjectContact Is Nothing Then
        Dim PostType As String = CType(IIf(PostID = RequestID, "Request", "Update"), String)
        If Request.SubjectContact.HasEmail Then
          Subject = GetText("Encouraging_EMail_New" & PostType & ".Subject")
          Body = GetText("Encouraging_EMail_New" & PostType & ".Body")
          SendMail(Request.SubjectContact.EMail, "", "", Subject, Body, "")
        ElseIf Request.SubjectContact.HasAddress Then
          Subject = GetText("Send_Encouraging_Card_New" & PostType & ".Subject")
          Body = GetText("Send_Encouraging_Card_New" & PostType & ".Body")
          SendMail(MyConfiguration.AdminEmail, "", "", Subject, Body, "")
        End If
        'Don't send twice if post is later edited.
        Dim epc As New EPrayerController
        epc.ClearNotifySubject(Post.PostID)
      End If
    End Sub

    'Send to all users who are either tracking any post to this module or any update to the particular request
    'to which this post is an update

    Private Sub SendTrackingEMails()

      If MyConfiguration.EnableNotification Then
        Dim PostType As String = CType(IIf(PostID = RequestID, "Request", "Update"), String)
        Recipients.Clear()
        Try
          Dim epc As New EPrayerController
          Dim TrackingEMails As Generic.List(Of String) = epc.GetTrackingEMails(PostID)
          If TrackingEMails.Count > 0 Then Recipients.AddRange(TrackingEMails)
        Catch exc As Exception
          Throw New DataException("Unable to obtain tracking emails for PostID=" & PostID.ToString, exc)
        End Try

        SendBulkEMail("Tracking_EMail_New" & PostType)
      End If
    End Sub

    'Send to all moderators
    Private Sub SendModeratorEMails()
      If MyConfiguration.EnableModeration Then
        Recipients.Clear()
        Try
          Dim epc As New EPrayerController
          Dim ModeratorEMails As Generic.List(Of String) = epc.GetModeratorEMails(PostID)
          If ModeratorEMails.Count > 0 Then Recipients.AddRange(ModeratorEMails)
        Catch exc As Exception
          Throw New DataException("Unable to obtain moderator emails for PostID=" & PostID.ToString, exc)
        End Try

        SendBulkEMail("Moderation_Requested_NewPost_EMail")
      End If
    End Sub

    Public Overloads Sub SendMail()

      Dim Subject As String
      Dim Body As String

      Select Case ModerationAction
        Case Enums.ModerationActions.None
          Select Case PostAction
            Case Enums.PostActions.NewRequest, Enums.PostActions.NewUpdate
              SendSubjectEMails()
              SendTrackingEMails()

            Case Enums.PostActions.EditRequest, Enums.PostActions.EditUpdate
              'No e-mails are sent

            Case Enums.PostActions.Delete
              'No e-mails are sent - maybe should go to admin and all who have posted in this request?

          End Select

        Case Enums.ModerationActions.Submit
          If PostAction = Enums.PostActions.NewRequest OrElse PostAction = Enums.PostActions.NewUpdate Then
            SendModeratorEMails()
            If Not _AuthorInfo.IsAnonymous Then
              Subject = GetText("Moderation_Pending_EMail.Subject")
              Body = GetText("Moderation_Pending_EMail.Body")
              SendMail(_AuthorInfo.Email, "", "", Subject, Body, "")
            End If
          End If

        Case Enums.ModerationActions.Approve, Enums.ModerationActions.EditApprove

          If Not _AuthorInfo.IsAnonymous Then
            Subject = GetText("Post_Approved_EMail.Subject")
            Body = GetText("Post_Approved_EMail.Body")
            SendMail(_AuthorInfo.Email, "", "", Subject, Body, "")
          End If

          SendSubjectEMails()
          SendTrackingEMails()

        Case Enums.ModerationActions.Reject
          If Not _AuthorInfo.IsAnonymous Then
            Subject = GetText("Post_Rejected_EMail.Subject")
            Body = GetText("Post_Rejected_EMail.Body")
            SendMail(_AuthorInfo.Email, "", "", Subject, Body, "")
          End If

        Case Enums.ModerationActions.Delete
          If Not _AuthorInfo.IsAnonymous Then
            Subject = GetText("Post_Deleted_EMail.Subject")
            Body = GetText("Post_Deleted_EMail.Body")
            SendMail(_AuthorInfo.Email, "", "", Subject, Body, "")
          End If
      End Select
    End Sub

#End Region

#Region "Private Methods"

    Private Function ModerationURL() As String
      Dim params() As String = New String() {"mid=" & _ModuleId.ToString}
      Return DotNetNuke.Common.Globals.NavigateURL(_TabID, "mod", params)
    End Function

    Private Function ViewPostURL() As String
      Dim params() As String = New String() {"mid=" & _ModuleId.ToString, "postid=" & _PostID.ToString}
      Return DotNetNuke.Common.Globals.NavigateURL(_TabID, "", params)
    End Function

    Private Overloads Function GetText(ByVal MessageKey As String) As String
      Dim MailStyleName As String = "TEXT"

      Dim EmailTemplateRoot As String = TemplatePath & "EMail"

      If MyConfiguration.MailFormat = DotNetNuke.Services.Mail.MailFormat.Html Then
        MailStyleName = MyConfiguration.MailStyleName
      End If

      Return GetText(MessageKey, EmailTemplateRoot, "", MailStyleName)
    End Function

    Private Overloads Function GetText(ByVal MessageKey As String, ByVal EMailTemplateRoot As String, _
                                       ByVal Language As String, ByVal MailStyleName As String) As String

      Dim MessageKeyParts() As String = MessageKey.Split("."c)
      If MessageKeyParts.Length <> 2 Then Return MessageKey

      Dim doc As XPath.XPathDocument = Utilities.OpenXMLTemplate(EMailTemplateRoot, Language)
      Dim XPathExpression As String
      Dim Msg As String
      Dim XPathNav As XPath.XPathNavigator = doc.CreateNavigator()
      Dim TemplateNode As XPath.XPathNavigator = XPathNav.SelectSingleNode("//Template[@Name='" & MessageKeyParts(0) & "']")

      Select Case MessageKeyParts(1)
        Case "Subject"
          XPathExpression = "Subject"
        Case "Body"
          XPathExpression = "Styles/Body[@Style='" & MailStyleName & "']"
        Case Else
          XPathExpression = MessageKey
      End Select

      Try
        Msg = TemplateNode.SelectSingleNode(XPathExpression).Value
      Catch exc As Exception
        Return String.Format(Localization.GetString("EMail_Template_Not_Found", MyConfiguration.LocalSharedResourceFile), MessageKey, MailStyleName)
      End Try

      If Not String.IsNullOrEmpty(Msg) Then

        Dim rgx As New Regex("(\[\s*(?:(?<obj>[A-Za-z_][A-Za-z0-9_$]*)\s*:)?\s*(?<prop>[A-Za-z_][A-Za-z0-9_$.]*)?\s*(?<fmt>\{0:.+?\})?\s*\])", RegexOptions.IgnoreCase Or RegexOptions.Compiled)
        Dim dict As New StringDictionary()
        Dim mc As MatchCollection
        mc = rgx.Matches(Msg)
        For Each m As Match In mc
          If m.Success Then
            Dim Tag As String = m.Groups(1).Value
            Dim objName As String = m.Groups("obj").Value
            Dim propName As String = m.Groups("prop").Value
            Dim fmtString As String = m.Groups("fmt").Value
            Dim Value As String = Nothing
            Select Case objName
              Case "Host"
                If _HostSettings.ContainsKey(propName) Then
                  If String.IsNullOrEmpty(fmtString) Then
                    Value = _HostSettings.Item(propName).ToString
                  Else
                    Value = String.Format(fmtString, _HostSettings.Item(propName))
                  End If
                End If
              Case "Portal"
                Select Case propName
                  Case "Logo"
                    Dim Logo As String = PortalSettings.LogoFile
                    If Not String.IsNullOrEmpty(Logo) Then
                      Value = PortalSettings.HomeDirectory & HttpContext.Current.Server.UrlPathEncode(Logo)
                    End If
                  Case "URL"
                    Dim URL As String = AddHTTP(PortalSettings.PortalAlias.HTTPAlias)
                  Case Else
                    Value = Utilities.GetPropertyText(PortalSettings, propName, fmtString)
                End Select
              Case "User"
                If propName = "VerificationCode" Then
                  Value = _PortalId.ToString & "-" & _UserID.ToString
                ElseIf propName.StartsWith("Profile.") Then
                  Value = GetUserProfileProperty(_UserInfo, propName, fmtString)
                Else
                  Value = Utilities.GetPropertyText(_UserInfo, propName, fmtString)
                End If
              Case "Author"
                If propName = "VerificationCode" Then
                  Value = _PortalId.ToString & "-" & _AuthorID.ToString
                ElseIf propName.StartsWith("Profile.") Then
                  Value = GetUserProfileProperty(_AuthorInfo, propName, fmtString)
                Else
                  Value = Utilities.GetPropertyText(_AuthorInfo, propName, fmtString)
                End If
              Case "Request"
                If propName = "IsPraise" Then
                  Value = Utilities.FormatPrayerOrPraise(CType(Utilities.GetPropertyValue(_Request, propName), Boolean), MyConfiguration.LocalSharedResourceFile)
                Else
                  Value = Utilities.GetPropertyText(_Request, propName, fmtString)
                End If
              Case "Post"
                If propName = "CreatedDate" Then propName = "CreatedOnDate" 'fixes issue of old email templates using deprecated CreatedDate token
                Value = Utilities.GetPropertyText(_Post, propName, fmtString)
              Case "Me"
                Value = Utilities.GetPropertyText(Me, propName, fmtString)
              Case ""
                Select Case propName
                  Case "Date"
                    If String.IsNullOrEmpty(fmtString) Then
                      Dim ci As CultureInfo
                      If Not String.IsNullOrEmpty(Language) Then
                        ci = New CultureInfo(Language)
                      Else
                        ci = New CultureInfo(System.Threading.Thread.CurrentThread.CurrentCulture.ToString.ToLower)
                      End If
                      Value = Now().ToString("D", ci)
                    Else
                      Value = String.Format(fmtString, Now())
                    End If
                  Case "Time"
                    If String.IsNullOrEmpty(fmtString) Then
                      Dim ci As CultureInfo
                      If Not String.IsNullOrEmpty(Language) Then
                        ci = New CultureInfo(Language)
                      Else
                        ci = New CultureInfo(System.Threading.Thread.CurrentThread.CurrentCulture.ToString.ToLower)
                      End If
                      Value = Now().ToString("t", ci)
                    Else
                      Value = String.Format(fmtString, Now())
                    End If
                  Case "ViewPostURL"
                    Value = ViewPostURL()
                  Case "ModerationURL"
                    Value = ModerationURL()
                  Case Else
                    Value = Utilities.GetPropertyText(Me, propName, fmtString)
                End Select
            End Select
            If Not Value Is Nothing AndAlso Not dict.ContainsKey(Tag) Then
              dict.Add(Tag, Value)
            End If
          End If
        Next

        For Each item As DictionaryEntry In dict
          Dim Key As String = CType(item.Key, String)
          Key = "\" & Key.Replace("]", "\]")
          Msg = Regex.Replace(Msg, Key, CType(item.Value, String), RegexOptions.IgnoreCase)
        Next

      End If
      Return Msg

    End Function

    Public Function GetUserProfileProperty(ByVal obj As UserInfo, ByVal propName As String, ByVal FormatString As String) As String
      Dim profile As DotNetNuke.Entities.Users.UserProfile = obj.Profile
      Dim Value As String = String.Empty
      If propName = "Fullname" Then
        Value = profile.FullName
      Else
        Dim prop As DotNetNuke.Entities.Profile.ProfilePropertyDefinition = profile.ProfileProperties.GetByName(propName)
        If Not prop Is Nothing Then
          Value = prop.PropertyValue
        End If
      End If
      If Not String.IsNullOrEmpty(FormatString) Then Value = String.Format(FormatString, Value)
      Return Value
    End Function

#End Region
  End Class
End Namespace